import { _decorator, Component, Prefab, Node, Graphics, Color, Vec2, instantiate, v2, Tween, misc, tween, isValid } from 'cc';
const { ccclass, property } = _decorator;

import bezier_data from "./bezier_data";
import bezier_point, { PointType } from "./bezier_point";
import { BezierUtils } from "./bezier_utils";
import { FishMgr } from '../scripts/game/fish/FishMgr';

@ccclass('BezierLine')
export default class bezier_line extends Component {
    @property(Prefab)
    private bezier_point_prefab: Prefab | null = null;
    @property(Node)
    private pointsLayer: Node | null = null;
    private _graphic: Graphics | null = null;

    progress = 0;
    speed = 1;
    is_addspeed = false;

    /** 所有点 */
    private _bezierPoints: bezier_point[] = [];
    public get bezierPoints(): bezier_point[] { return this._bezierPoints; }
    protected onLoad() {
        this.node.on('pointSelect', this.onPointSelect, this);
        this.node.on('pointMove', this.onPointMove, this);
        this.node.on('lineMove', this.onLineMove, this);
        this.node.on('pointAdd', this.onPointAdd, this);
        this.node.on('pointDel', this.onPointDel, this);
        this._graphic = this.node.getChildByName('graphic').getComponent(Graphics);
    }
    public init() {
        // 添加起点*1
        this.addPoint(true);
        // 添加控制点*2，终点*1
        this.addPoint();
        // 改变当前贝塞尔曲线焦点为自身
        this.changeCurBezierLineToSelf();
        // 调用第一次绘制
        bezier_data.bezier_scene.draw();
    }
    public draw() {
        this._graphic.lineWidth = 6;
        if (bezier_data.bezierLines[bezier_data.curBezierLineIndex] === this) {
            this._graphic.strokeColor = new Color().fromHEX('#0CBAD6');
        } else {
            this._graphic.strokeColor = new Color().fromHEX('#0C3DD6');
        }
        this._graphic.clear();
        const fp = this._bezierPoints[0];
        this._graphic.moveTo(fp.node.x, fp.node.y);
        for (let i = 1; i < this._bezierPoints.length; i += 3) {
            const cp0 = this._bezierPoints[i];
            const cp1 = this._bezierPoints[i + 1];
            const ep = this._bezierPoints[i + 2];
            this._graphic.bezierCurveTo(cp0.node.x, cp0.node.y, cp1.node.x, cp1.node.y, ep.node.x, ep.node.y);
        }
        this._graphic.stroke();
        if (bezier_data.drawGuideLines) {
            this._graphic.strokeColor = Color.RED;
            this._graphic.lineWidth = 4;
            for (let i = 0; i < this._bezierPoints.length; i += 3) {
                const sp = this._bezierPoints[i];
                if (i + 1 < this._bezierPoints.length) {
                    const cp0 = this._bezierPoints[i + 1];
                    this._graphic.moveTo(sp.node.x, sp.node.y);
                    this._graphic.lineTo(cp0.node.x, cp0.node.y);
                }
                if (i + 3 < this._bezierPoints.length) {
                    const cp1 = this._bezierPoints[i + 2];
                    const ep = this._bezierPoints[i + 3];
                    this._graphic.moveTo(cp1.node.x, cp1.node.y);
                    this._graphic.lineTo(ep.node.x, ep.node.y);
                }
            }
            this._graphic.stroke();
        }
    }
    public addPoint(isStartPoint = false) {
        if (isStartPoint) {
            // 整个曲线的第一个点
            const pNode = instantiate(this.bezier_point_prefab);
            pNode.parent = this.pointsLayer;
            const pComp = pNode.getComponent(bezier_point);
            pComp.init(0, PointType.PATH, this);
            this._bezierPoints.push(pComp);
        } else {
            // 先取出最后一个路径点
            const lastPathPoint = this._bezierPoints[this._bezierPoints.length - 1];
            const lastPathPos = lastPathPoint.node.getPosition().toVec2();
            // 尝试取出最后一个控制点
            const lastCtrlPoint = this._bezierPoints[this._bezierPoints.length - 2];
            let ctrlPos0 = v2(lastPathPos.x + 30, lastPathPos.y);
            // 如果存在最后一个控制点，则根据最后的路径点+控制点，生成对称的第一个控制点
            if (lastCtrlPoint) ctrlPos0 = this.getPairPointPos(lastCtrlPoint.node.getPosition().toVec2(), lastPathPos);
            // c0
            const cNode0 = instantiate(this.bezier_point_prefab);
            cNode0.parent = this.pointsLayer;
            cNode0.setPosition(ctrlPos0.toVec3());
            const cComp0 = cNode0.getComponent(bezier_point);
            cComp0.init(this._bezierPoints.length, PointType.CTRL, this);
            this._bezierPoints.push(cComp0);
            // c1
            const cNode1 = instantiate(this.bezier_point_prefab);
            cNode1.parent = this.pointsLayer;
            cNode1.setPosition(lastPathPos.x + 60, lastPathPos.y);
            const cComp1 = cNode1.getComponent(bezier_point);
            cComp1.init(this._bezierPoints.length, PointType.CTRL, this);
            this._bezierPoints.push(cComp1);
            // 最后放入一个路径点
            const pNode = instantiate(this.bezier_point_prefab);
            pNode.setPosition(lastPathPos.x + 90, lastPathPos.y);
            pNode.parent = this.pointsLayer;
            const pComp = pNode.getComponent(bezier_point);
            pComp.init(this._bezierPoints.length, PointType.PATH, this);
            this._bezierPoints.push(pComp);
        }
    }
    
    public initWithPositionArray(posArray: [number, number][]) {
        this.addPoint(true);
        for (let i = 1; i < posArray.length; i += 3) {
            this.addPoint();
        }
        for (let i = 0; i < this._bezierPoints.length; ++i) {
            this._bezierPoints[i].node.setPosition(posArray[i][0], posArray[i][1]);
        }
    }
    
    public initWithPositionArray_any(posArray: any) {
        this.addPoint(true);
        for (let i = 1; i < posArray.length; i += 3) {
            this.addPoint();
        }
        for (let i = 0; i < this._bezierPoints.length; ++i) {
            this._bezierPoints[i].node.setPosition(posArray[i][0], posArray[i][1]);
        }
    }

    public run(runSpeed: number = 300 ) {
        const yellowCircleNode = this.createTriangle();
        const BezierPoints = this._bezierPoints.map(v => { return { x: v.node.x, y: v.node.y } });
        const {
            total: totalLineLength,
            array: lineLengthArray
        } = BezierUtils.getLineLengthArray(BezierPoints);
        const totalRunTime = totalLineLength / runSpeed;
        let runTime = 0;
        const ss = (dt: number) => {
            if (runTime > totalRunTime) {
                const endPos = BezierPoints[BezierPoints.length - 1];
                yellowCircleNode.setPosition(endPos.x, endPos.y);
                this.scheduleOnce(() => yellowCircleNode.destroy(), 1);
                this.unschedule(ss);
                return;
            }
            const lengthPassed = runTime / totalRunTime * totalLineLength;
            const {
                passed,
                points
            } = BezierUtils.getCurSegInfo(BezierPoints, lengthPassed);

            const rt = BezierUtils.t2rt(points, passed);
            const { x, y } = BezierUtils.getPosition(points, rt);
            const angle = BezierUtils.getDirection(points, rt);
            yellowCircleNode.setPosition(x, y);
            yellowCircleNode.angle = misc.radiansToDegrees(angle);

            runTime += dt;
        };
        this.schedule(ss);
        // if (BezierPoints.length > 0) {
        //     // 生成临时节点
        //     const tempNode = this.createYellowCircle(10, Color.GREEN);
        //     const segmentTime = totalRunTime / lineLengthArray.length;

        //     let t0 = tween(tempNode);
        //     for (let i = 0; i < BezierPoints.length - 3; i += 3) {
        //         const p0 = BezierPoints[i];
        //         const c0 = BezierPoints[i + 1];
        //         const c1 = BezierPoints[i + 2];
        //         const p1 = BezierPoints[i + 3];
        //         const t = tween(tempNode).set(p0).bezierTo(segmentTime, v2(c0), v2(c1), v2(p1));
        //         t0 = t0.then(t);
        //     }
        //     const tEnd = tween(tempNode).call(() => tempNode.destroy());
        //     t0.then(tEnd).start();
        // }
    }
    
    /** ---> 新 运行节点 */
    public runNode(runSpeed: number  ,fishNode:Node ,mid_start = false , addspeed = 2) {
        // console.log(" ---> " , this._bezierPoints)
        const BezierPoints = this._bezierPoints.map(v => { return { x: v.node.x, y: v.node.y } });
        // console.log(BezierPoints)
        const {
            total: totalLineLength,
            array: lineLengthArray
        } = BezierUtils.getLineLengthArray(BezierPoints);
        // console.log(totalLineLength)

        const totalRunTime = totalLineLength / runSpeed;
        let runTime = 0;

        if(mid_start)runTime = totalRunTime * 0.3;

        //console.log(this.node.uuid)

        const ss = (dt: number) => {

            if (runTime > totalRunTime) {
                const endPos = BezierPoints[BezierPoints.length - 1];
                fishNode.setPosition(endPos.x, endPos.y);
                this.unschedule(ss);
                // console.log('结束游动')
                FishMgr.Inst.runRestart(runSpeed, fishNode);
                return;
            }

            // console.log(totalRunTime , totalLineLength)

            const lengthPassed = runTime / totalRunTime * totalLineLength;
            this.progress = lengthPassed / totalLineLength;
            //console.log('进度：',this.progress)
            const {
                passed,
                points
            } = BezierUtils.getCurSegInfo(BezierPoints, lengthPassed);

            const rt = BezierUtils.t2rt(points, passed);
            const { x, y } = BezierUtils.getPosition(points, rt);
            const angle = BezierUtils.getDirection(points, rt);

            fishNode.setPosition(x , y)
            fishNode.position.add(this.node.getPosition())
            fishNode.angle = misc.radiansToDegrees(angle);

            // if (this.is_addspeed) {
            //     this.speed += dt;
            //     if (this.speed > addspeed) {
            //         this.speed = addspeed;
            //     }
            // }
            // else {
            //     this.speed -= dt;
            //     if (this.speed < 1) {
            //         this.speed = 1;
            //     }
            // }

            let newdt = dt * this.speed;
            runTime += newdt;
        };
        this.schedule(ss);
    }
    
    private createYellowCircle(size: number = 10, color: Color = Color.YELLOW) {
        const tempNode = new Node();
        tempNode.parent = this.node;
        tempNode.setPosition(this._bezierPoints[0].node.getPosition());
        const tempG = tempNode.addComponent(Graphics);
        tempG.strokeColor = color;
        tempG.fillColor = color;
        tempG.circle(0, 0, size);
        tempG.fill();
        tempG.stroke();

        return tempNode;
    }
    private createTriangle(size: number = 20, color: Color = Color.YELLOW) {
        const tempNode = new Node();
        tempNode.parent = this.node;
        tempNode.setPosition(this._bezierPoints[0].node.getPosition());
        const tempG = tempNode.addComponent(Graphics);
        tempG.strokeColor = color;
        tempG.fillColor = color;
        tempG.moveTo(-size, size / 2);
        tempG.lineTo(-size, -size / 2);
        tempG.lineTo(size, 0);
        tempG.close();
        tempG.fill();
        tempG.stroke();

        return tempNode;
    }
    private delPoint(pIndex: number) {
        if (pIndex !== undefined && pIndex % 3 === 0) {
            // 如果是起始点，则删除 0/1/2三个点
            if (pIndex === 0) pIndex = 1;
            // 如果是终点，则删除 最后三个点
            if (pIndex === this._bezierPoints.length - 1) pIndex = this._bezierPoints.length - 2;
            // 从后往前依次删除三个点
            if (pIndex + 1 < this._bezierPoints.length) {
                const p2 = this._bezierPoints.splice(pIndex + 1, 1);
                p2[0].node.destroy();
            }
            const p1 = this._bezierPoints.splice(pIndex, 1);
            p1[0].node.destroy();
            if (pIndex - 1 >= 0) {
                const p0 = this._bezierPoints.splice(pIndex - 1, 1);
                p0[0].node.destroy();
            }
            // 更新序列(鉴于整体长度不多，直接整体更新)
            for (let i = 0; i < this._bezierPoints.length; ++i) {
                this._bezierPoints[i].updateIndex(i);
            }
        } else {
            for (let i = 0; i < 3; ++i) {
                const p = this._bezierPoints.pop()
                p.node.destroy();
            }
        }
    }
    private onPointSelect() {
        this.changeCurBezierLineToSelf();
        bezier_data.bezier_scene.draw();
    }
    private onPointMove(index: number, delta: Vec2) {
        const dragged_point = this._bezierPoints[index];
        if (dragged_point.pointType === PointType.PATH) {
            if (index !== 0 && index !== this._bezierPoints.length - 1) {
                const ctrl_point_0 = this._bezierPoints[index - 1];
                const ctrl_point_1 = this._bezierPoints[index + 1];
                ctrl_point_0.node.x += delta.x;
                ctrl_point_0.node.y += delta.y;
                ctrl_point_1.node.x += delta.x;
                ctrl_point_1.node.y += delta.y;
            }
        } else {
            if (index > 1 && index < this._bezierPoints.length - 2) {
                const isLeftCtrlPoint = index % 3 === 1;
                const pathPoint = isLeftCtrlPoint ? this._bezierPoints[index - 1] : this._bezierPoints[index + 1];
                // 防止控制点与路径点重合，导致失去方向向量
                const pairCtrlPoint = isLeftCtrlPoint ? this._bezierPoints[index - 2] : this._bezierPoints[index + 2];
                const pairNewPos = this.getPairPointPos(dragged_point.node.getPosition().toVec2(), pathPoint.node.getPosition().toVec2());
                pairCtrlPoint.node.setPosition(pairNewPos.toVec3());
            }
        }
        this.changeCurBezierLineToSelf();
        bezier_data.bezier_scene.draw();
    }
    private onLineMove(delta: Vec2) {
        this.node.x += delta.x;
        this.node.y += delta.y;
    }
    private onPointAdd() {
        this.addPoint();
        this.changeCurBezierLineToSelf();
        bezier_data.bezier_scene.draw();
    }
    private onPointDel(pIndex: number) {
        this.changeCurBezierLineToSelf();
        if (this._bezierPoints.length > 4) {
            this.delPoint(pIndex);
            bezier_data.bezier_scene.draw();
        } else {
            bezier_data.bezier_scene.delCurBezierLine();
        }
    }
    private changeCurBezierLineToSelf() {
        const selfIndex = bezier_data.bezierLines.indexOf(this);
        if (selfIndex >= 0) bezier_data.curBezierLineIndex = selfIndex;
    }
    private getPairPointPos(aPos: Vec2, midPos: Vec2) {
        return v2(midPos.x * 2 - aPos.x, midPos.y * 2 - aPos.y);
    }
    protected onDestroy() {
        this.stopRun();
    }

    stopRun(){
        this.unscheduleAllCallbacks();
    }
}
